#include "Object.h"
#include "Geometry.h"
#include "InternalMaterial.h"
#include "Material.h"
#include <iostream>
using namespace Umikaze::Core;

glm::mat4 Object::genModelMatrix() {
  glm::mat4 modelMatrix(1.0f);
  modelMatrix = glm::translate(modelMatrix, _position);
  modelMatrix = glm::rotate(modelMatrix, glm::radians(_rotation.y),
                            glm::vec3(0.0f, 1.0f, 0.0f));
  modelMatrix = glm::rotate(modelMatrix, glm::radians(_rotation.x),
                            glm::vec3(1.0f, 0.0f, 0.0f));
  modelMatrix = glm::rotate(modelMatrix, glm::radians(_rotation.z),
                            glm::vec3(0.0f, 0.0f, 1.0f));
  modelMatrix = glm::scale(modelMatrix, _scale);
  return modelMatrix;
}

Object::Object(const glm::vec3 &position, const glm::vec3 &rotation,
               const glm::vec3 &scale) {
  _position = position;
  _rotation = rotation;
  _scale = scale;

  auto mat = genModelMatrix();
  _oldMatrix = _newMatrix = mat;
}

Object::Object(const std::shared_ptr<Geometry> &geometry,
               const std::shared_ptr<Material> &material,
               const glm::vec3 &position, const glm::vec3 &rotation,
               const glm::vec3 &scale)
    : Object::Object(position, rotation, scale) {
  _geometry = geometry;
  _material = material;
}

void Object::drawMotionVector(const std::shared_ptr<Buffer> &cameraBuffer,
                              const glm::mat4 &oldView,
                              const glm::mat4 &oldProj) {
  static std::unique_ptr<MotionVectorMaterial> motionVectorMaterial =
      std::make_unique<MotionVectorMaterial>();

  if (_dirty) {
    auto modelMatrix = genModelMatrix();
    _oldMatrix = _newMatrix;
    _newMatrix = modelMatrix;
    _dirty = false;
  }
  motionVectorMaterial->bind(_newMatrix, _oldMatrix, oldView, oldProj,
                             cameraBuffer);
  _geometry->draw();
}

void Object::drawShadow(const glm::mat4 &lightSpaceMatrix) {
  static std::unique_ptr<DrawShadowMaterial> drawShaodwMaterial =
      std::make_unique<DrawShadowMaterial>();
  if (_dirty) {
    auto modelMatrix = genModelMatrix();
    _oldMatrix = _newMatrix;
    _newMatrix = modelMatrix;
    _dirty = false;
  }
  drawShaodwMaterial->bind(_newMatrix, lightSpaceMatrix);
  _geometry->draw();
}

void Object::drawShadowCascade(
    const std::shared_ptr<Buffer> &lightSpaceMatrices, uint32_t matrixCount) {
  static std::unique_ptr<DrawShadowCascadeMaterial> drawShadowCascadeMaterial =
      std::make_unique<DrawShadowCascadeMaterial>();
  if (_dirty) {
    auto modelMatrix = genModelMatrix();
    _oldMatrix = _newMatrix;
    _newMatrix = modelMatrix;
    _dirty = false;
  }
  drawShadowCascadeMaterial->bind(_newMatrix, lightSpaceMatrices, matrixCount);
  _geometry->draw();
}

void Object::draw(const std::shared_ptr<Buffer> &cameraBuffer) {
  if (_dirty) {
    auto modelMatrix = genModelMatrix();
    _oldMatrix = _newMatrix;
    _newMatrix = modelMatrix;
    _dirty = false;
  }
  _material->bind(_newMatrix, cameraBuffer);
  _geometry->draw();
}

void Object::drawDepth(const std::shared_ptr<Buffer> &cameraBuffer) {
  static std::unique_ptr<DrawDepthMaterial> drawDepthMaterial =
      std::make_unique<DrawDepthMaterial>();
  if (_dirty) {
    auto modelMatrix = genModelMatrix();
    _oldMatrix = _newMatrix;
    _newMatrix = modelMatrix;
    _dirty = false;
  }
  drawDepthMaterial->bind(_newMatrix, cameraBuffer);
  _geometry->draw();
}

void Object::rotate() {
  // _dirty = true;
}

void Object::translate(float x, float y, float z) {
  _position.x += x;
  _position.y += y;
  _position.z += z;

  _dirty = true;
}

void Object::translate(const glm::vec3 &t) {
  _position += t;
  _dirty = true;
}